/*****************************************************************************/
/*                                                                           */
/*    Domino Operation System Driver Module                                  */
/*                                                                           */
/*    Copyright (C) 2007 Laszlo Arvai                                        */
/*                                                                           */
/*    ------------------------------------------------------------------     */
/*    drvSPI - SPI Driver for AVR series                                     */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Includes
#include <krnlTypes.h>
#include <krnlSystemTimer.h>
#include <krnlCAN.h>
#include "drvIOConfig.h"
#include "drvMCP2515.h"
#include "drvSPI.h"

///////////////////////////////////////////////////////////////////////////////
// Constants
#define REGISTER_BUFFER_LENGTH 16

///////////////////////////////////////////////////////////////////////////////
// Initialize CAN driver
void krnlCANInit(void)
{
	// set control pin directions
	InitMCP2515RESET();
	InitMCP2515INT();
	InitMCP2515CS();

	// init SPI
	drvSPIInit();
	
	// init pins
	MCP2515CS( PIN_HIGH );

	// reset MCP2515
#ifdef MCP2515RESET
	// HW reset if reset pin avaiable
	MCP2515RESET( PIN_LOW );		// reset mcp2515
	krnlDelay( 2 );							// 2ms delay
	MCP2515RESET( PIN_HIGH );		// deactivate reset
#else
	// software reset if reset pin is not avaiable
	MCP2515CS( PIN_LOW );
	drvSPISendAndReceiveByte( drvMCP2515CMD_RESET );		// Write the command
	MCP2515CS( PIN_HIGH );
#endif

	// enter config mode
  drvMCP2515BitModify( drvMCP2515RA_CANCTRL, drvMCP2515BIT_MODE_MASK, drvMCP2515BIT_MODE_CONFIG );

	// set clockout
	drvMCP2515BitModify( drvMCP2515RA_CANCTRL, drvMCP2515BIT_CLK_MASK, MCP2515DEF_CLOCKOUT );

	// Deactivate RXnBF Pins (High Impedance State)
  drvMCP2515WriteRegister( drvMCP2515RA_BFPCTRL, 0 );
    
	// TXnRTS Bits are Inputs
	drvMCP2515WriteRegister( drvMCP2515RA_TXRTSCTRL, 0 );

	// set bitrate
	drvMCP2515WriteRegister( drvMCP2515RA_CNF1, MCP2515DEF_CNF1 );
    
  // set Prop Seg and Phase Seg1
  drvMCP2515WriteRegister( drvMCP2515RA_CNF2, MCP2515DEF_CNF2 );
    
	// set Wake-up Filter and Phase Seg2
	drvMCP2515WriteRegister( drvMCP2515RA_CNF3, MCP2515DEF_CNF3 );
    
	// Activate Rx Buffer Interrupts
#ifdef MCP2515INT
	drvMCP2515WriteRegister( drvMCP2515RA_CANINTE, drvMCP2515BIT_RX1IE | drvMCP2515BIT_RX0IE );
#endif
	
	// setup filters
    
	// Buffer 0 : receive all messages
	drvMCP2515WriteRegister( drvMCP2515RA_RXB0CTRL, drvMCP2515BIT_RXB_RXM1 | drvMCP2515BIT_RXB_RXM0 );
    
  // Buffer 1 : receive all mesasges
	drvMCP2515WriteRegister( drvMCP2515RA_RXB1CTRL, drvMCP2515BIT_RXB_RXM1 | drvMCP2515BIT_RXB_RXM0 );
    
  // clear masks
	drvMCP2515WriteRegister( drvMCP2515RA_RXM0SIDH, 0 );
	drvMCP2515WriteRegister( drvMCP2515RA_RXM0SIDL, 0 );
	drvMCP2515WriteRegister( drvMCP2515RA_RXM0EID8, 0 );
	drvMCP2515WriteRegister( drvMCP2515RA_RXM0EID0, 0 );

	drvMCP2515WriteRegister( drvMCP2515RA_RXM1SIDH, 0 );
	drvMCP2515WriteRegister( drvMCP2515RA_RXM1SIDL, 0 );
	drvMCP2515WriteRegister( drvMCP2515RA_RXM1EID8, 0 );
	drvMCP2515WriteRegister( drvMCP2515RA_RXM1EID0, 0 );

	krnlCANBusOn();
}

///////////////////////////////////////////////////////////////////////////////
// Kernel level functions
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Is message avaiable
dosBool krnlCANIsMessageAvailable(void)
{
#ifdef MCP2515INT
	return MCP2515INT() == 0;
#else
	return (drvMCP2515ReadStatus() & (drvMCP2515BIT_RS_RX0IF | drvMCP2515BIT_RS_RX1IF)) != 0;
#endif
}

///////////////////////////////////////////////////////////////////////////////
// Connect to the bus
void krnlCANBusOn(void)
{
  drvMCP2515BitModify( drvMCP2515RA_CANCTRL, drvMCP2515BIT_MODE_MASK, drvMCP2515BIT_MODE_NORMAL );
}

///////////////////////////////////////////////////////////////////////////////
// Disconnect from the bus
void krnlCANBusOff(void)
{
  drvMCP2515BitModify( drvMCP2515RA_CANCTRL, drvMCP2515BIT_MODE_MASK, drvMCP2515BIT_MODE_CONFIG );
}

///////////////////////////////////////////////////////////////////////////////
// Get Message
dosBool krnlCANGetMessage( krnlCANMessage *out_message )
{
	dosByte buffer[REGISTER_BUFFER_LENGTH];
	dosByte i;
	dosByte status;

	// init
	out_message->Flags = 0;

	// get status
	status = drvMCP2515ReadStatus();

	if((status & drvMCP2515BIT_RS_RX0IF) != 0 )
	{
		// message in buffer 0
		drvMCP2515ReadRegisters( drvMCP2515RA_RXB0CTRL, buffer, 16 );
	}
	else 
	{
		if((status & drvMCP2515BIT_RS_RX1IF) != 0)
		{
			// message in buffer 1
			drvMCP2515ReadRegisters( drvMCP2515RA_RXB1CTRL, buffer, 16 );
		}
		else
		{
			return dosFalse;
		}
	}

	// Obtain the first 11 identifier bits...
  out_message->Id = ((dosWord)(buffer[drvMCP2515RA_RXBnSIDH]) << 3) + ( buffer[drvMCP2515RA_RXBnSIDL]>>5 );
    
  // ...and the message length...
	out_message->Length = buffer[drvMCP2515RA_RXBnDLC] & 0x0F;

  // Is this a remote request?
  // There is one bit in MCP2515RA_RXBnDLC and another one in MCP2515RA_RXBnSIDL. Then
  // there is one in MCP2515RA_RXBnCTRL and that is the one we will use.
  if((buffer[drvMCP2515RA_RXBnCTRL] & drvMCP2515BIT_RXB_RXRTR) != 0)
     out_message->Flags |= krnlCANF_RTR;

  // If it's an extended frame, dig out the remaining 18 identifier bits.
  if((buffer[drvMCP2515RA_RXBnSIDL] & drvMCP2515BIT_RXB_IDE) !=  0)
	{
		out_message->Id = (out_message->Id<<2) + (buffer[drvMCP2515RA_RXBnSIDL] & 0x03);
		out_message->Id <<= 16;
    out_message->Id += (buffer[drvMCP2515RA_RXBnEID8]<<8) + buffer[drvMCP2515RA_RXBnEID0];

		out_message->Flags |= krnlCANF_EXTENDED;
	}

	// copy receiver data from buffer    
	for( i=drvMCP2515RA_RXBnD0; i <= drvMCP2515RA_RXBnD7; i++ )
	{
  	out_message->Data[i-drvMCP2515RA_RXBnD0] = buffer[i];
	}

	// clear interrupt flag
	if( (status & drvMCP2515BIT_RS_RX0IF) != 0 )
	{
		drvMCP2515BitModify( drvMCP2515RA_CANINTF, drvMCP2515BIT_RX0IF, 0 );
	} 
	else
	{
		drvMCP2515BitModify( drvMCP2515RA_CANINTF, drvMCP2515BIT_RX1IF, 0 );
	}

	return dosTrue;
}

///////////////////////////////////////////////////////////////////////////////
// Get TX Error
dosBool krnlCANIsTXError(void)
{
	// check TX buffer 0
	dosByte control = drvMCP2515ReadRegister( drvMCP2515RA_TXB0CTRL );
	
	// check for error on buffer 0
	if( (control & drvMCP2515BIT_TXB_TXERR) != 0 )
	{
		// clear error and return true
		drvMCP2515BitModify( drvMCP2515RA_TXB0CTRL, drvMCP2515BIT_TXB_TXREQ, 0 );
		
		return dosTrue;
	}
	
	// check for error on buffer 1
	control = drvMCP2515ReadRegister( drvMCP2515RA_TXB1CTRL );
	if( (control & drvMCP2515BIT_TXB_TXERR) != 0 )
	{
		// clear error and return true
		drvMCP2515BitModify( drvMCP2515RA_TXB1CTRL, drvMCP2515BIT_TXB_TXREQ, 0 );
		
		return dosTrue;
	}

	// check for error on buffer 2
	control = drvMCP2515ReadRegister( drvMCP2515RA_TXB2CTRL );
	if( (control & drvMCP2515BIT_TXB_TXERR) != 0 )
	{
		// clear error and return true
		drvMCP2515BitModify( drvMCP2515RA_TXB2CTRL, drvMCP2515BIT_TXB_TXREQ, 0 );
		
		return dosTrue;
	}

	return dosFalse;
}

///////////////////////////////////////////////////////////////////////////////
// Is TX buffer empty (ready for sending)
dosBool krnlCANIsTxEmpty(void)
{
	dosByte status;
	
	// get status
	status = drvMCP2515ReadStatus();

	// get empty buffer address
	return (status & (drvMCP2515BIT_RS_TX0REQ | drvMCP2515BIT_RS_TX1REQ | drvMCP2515BIT_RS_TX2REQ) != (drvMCP2515BIT_RS_TX0REQ | drvMCP2515BIT_RS_TX1REQ | drvMCP2515BIT_RS_TX2REQ));
}

///////////////////////////////////////////////////////////////////////////////
// CAN send message
dosBool krnlCANSendMessage( krnlCANMessage *in_message )
{
	dosByte buffer[REGISTER_BUFFER_LENGTH];
	dosByte i;
	dosByte status;
	dosByte buffer_id;
	dosWord canid;
	dosByte address;

	// reset buffer
	for( i = 0; i < REGISTER_BUFFER_LENGTH ; i++ )
		buffer[i] = 0;

	// get status
	status = drvMCP2515ReadStatus();

	// get empty buffer address
	if( (status & drvMCP2515BIT_RS_TX0REQ) == 0 )
	{
		buffer_id = 0;
	}
	else
	{
		if( (status & drvMCP2515BIT_RS_TX1REQ) == 0 )
		{
    	buffer_id = 1;
    } 
    else
		{
			if( (status & drvMCP2515BIT_RS_TX2REQ) == 0 )
			{
      	buffer_id = 2;
    	}
	    else
			{
				// there is no empty buffer
    		return dosFalse;
			}
    }
	}

  // Move data to a temporary buffer
	for(i = drvMCP2515RA_TXBnD0; i <= drvMCP2515RA_TXBnD7; i++ )
	{
		buffer[i] = in_message->Data[i-drvMCP2515RA_TXBnD0];
	}

  // Then, set up the priority/identifier of the message,
  // and whether the identifier is extended (29 bits) or
  // standard (11 bits)
  canid = (dosWord)in_message->Id;
  if( ( in_message->Flags & krnlCANF_EXTENDED ) != 0 )
	{
		buffer[drvMCP2515RA_TXBnEID0] = (dosByte)(canid & 0xFF);
		buffer[drvMCP2515RA_TXBnEID8] = (dosByte)(canid >> 8);
		canid = (dosWord)( in_message->Id >> 16 );
		buffer[drvMCP2515RA_TXBnSIDL] = (dosByte)(canid & 0x03);
		buffer[drvMCP2515RA_TXBnSIDL] |= drvMCP2515BIT_TXB_EXIDE;
		buffer[drvMCP2515RA_TXBnSIDL] |= (dosByte)(canid << 3);
		buffer[drvMCP2515RA_TXBnSIDH] = (dosByte)(canid >> 5 );
  }
  else
	{
		buffer[drvMCP2515RA_TXBnSIDH] = (dosByte)(canid >> 3 );
		buffer[drvMCP2515RA_TXBnSIDL] = (dosByte)((canid & 0x07) << 5);
  }

  // Set the RTR bit and in_data length of the message
  if( (in_message->Flags & krnlCANF_RTR) != 0 )
	{
		buffer[drvMCP2515RA_TXBnDLC] = drvMCP2515BIT_TXB_RTR;
  }
	else
	{
		buffer[drvMCP2515RA_TXBnDLC] = 0;
  }
  
  buffer[drvMCP2515RA_TXBnDLC] |= in_message->Length;

	// calculate address
	address = drvMCP2515_TRANSMIT_BUFFER(buffer_id) + 1;

  // Write data to the transmit buffer
  drvMCP2515WriteRegisters( address, &buffer[drvMCP2515RA_TXBnSIDH], 13 );

	// Send buffer contents
	drvMCP2515RequesToSend( 1 << buffer_id );
	
	
	i = drvMCP2515ReadRegister(0x30);

	return dosTrue;
}

///////////////////////////////////////////////////////////////////////////////
// Driver level functions
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Sets bit rate.
void drvMCP2515SetBitrate( dosByte in_btr0, dosByte in_btr1, dosByte in_btr2)
{
	// Go into configuration mode
	drvMCP2515BitModify( drvMCP2515RA_CANCTRL, drvMCP2515BIT_MODE_MASK, drvMCP2515BIT_MODE_CONFIG );

	// modify baud rate registers
	drvMCP2515WriteRegister( drvMCP2515RA_CNF1, in_btr0 );
	drvMCP2515WriteRegister( drvMCP2515RA_CNF2, in_btr1 );
	drvMCP2515WriteRegister( drvMCP2515RA_CNF3, in_btr2 );
}


///////////////////////////////////////////////////////////////////////////////
// Get RX Status
dosByte drvMCP2515ReadRXStatus(void)
{
	dosByte data;
    
  // activate CS
	MCP2515CS( PIN_LOW );
    
	// read status data
	drvSPISendAndReceiveByte( drvMCP2515CMD_RX_STAT );
 	data = drvSPISendAndReceiveByte(0xff);
    
	// deactivate CS
  MCP2515CS( PIN_HIGH );
    
  return data;
}

///////////////////////////////////////////////////////////////////////////////
// Read Status
dosByte drvMCP2515ReadStatus(void)
{
	dosByte data;
    
  // activate CS
	MCP2515CS( PIN_LOW );
    
	// read status data
	drvSPISendAndReceiveByte( drvMCP2515CMD_RD_STAT );
 	data = drvSPISendAndReceiveByte(0xff);
    
	// deactivate CS
  MCP2515CS( PIN_HIGH );
    
  return data;
}

///////////////////////////////////////////////////////////////////////////////
// Request to send
void drvMCP2515RequesToSend( dosByte in_flags )
{
  // activate CS
	MCP2515CS( PIN_LOW );
    
	// read status data
	drvSPISendAndReceiveByte( drvMCP2515CMD_RTS | (in_flags & 0x07));
    
	// deactivate CS
  MCP2515CS( PIN_HIGH );
}

///////////////////////////////////////////////////////////////////////////////
// Register access functions
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Write register
void drvMCP2515WriteRegister( dosByte in_address, dosByte in_data )
{
	// activate CS
	MCP2515CS(PIN_LOW);

	drvSPISendAndReceiveByte( drvMCP2515CMD_WRITE );
	drvSPISendAndReceiveByte( in_address );
	drvSPISendAndReceiveByte( in_data );

	// remove CS
	MCP2515CS(PIN_HIGH);
}

///////////////////////////////////////////////////////////////////////////////
// Read register
dosByte drvMCP2515ReadRegister(dosByte in_address)
{
	dosByte data;
    
	// activate CS
	MCP2515CS(PIN_LOW);
    
	drvSPISendAndReceiveByte( drvMCP2515CMD_READ );
	drvSPISendAndReceiveByte( in_address );
    
	data = drvSPISendAndReceiveByte( 0xff );  
    
	// remove CS
	MCP2515CS(PIN_HIGH);
    
  return data;
}

///////////////////////////////////////////////////////////////////////////////
// Register bit modification
void drvMCP2515BitModify( dosByte in_address, dosByte in_mask, dosByte in_data)
{
	// activate CS
	MCP2515CS(PIN_LOW);
    
	drvSPISendAndReceiveByte( drvMCP2515CMD_BITOD );
	drvSPISendAndReceiveByte( in_address );
	drvSPISendAndReceiveByte( in_mask );
	drvSPISendAndReceiveByte( in_data );
    
	// remove CS
	MCP2515CS(PIN_HIGH);
}

///////////////////////////////////////////////////////////////////////////////
// Read one or more registers in the MCP2515.
void drvMCP2515ReadRegisters( dosByte in_address, dosByte* in_buffer, dosByte in_buffer_length)
{
	dosByte i;

	// activate CS
	MCP2515CS(PIN_LOW);

	drvSPISendAndReceiveByte( drvMCP2515CMD_READ );
	drvSPISendAndReceiveByte( in_address );

	for(i=0; i < in_buffer_length; i++)
	{
		*in_buffer++  = drvSPISendAndReceiveByte(0xff);
	}

	// remove CS
	MCP2515CS(PIN_HIGH);
}

///////////////////////////////////////////////////////////////////////////////
// Write to one or more registers in the MCP2515.
void drvMCP2515WriteRegisters( dosByte in_address, dosByte* in_buffer, dosByte in_buffer_length)
{
	dosByte i;

	// activate CS
	MCP2515CS(PIN_LOW);

	drvSPISendAndReceiveByte( drvMCP2515CMD_WRITE );
	drvSPISendAndReceiveByte( in_address );

	for (i=0; i < in_buffer_length; i++)
	{
		drvSPISendAndReceiveByte(*in_buffer++);
	}

	// remove CS
	MCP2515CS(PIN_HIGH);
}

